from typing import Tuple

import wx
import wx.dataview

from event_bus import EventBus
from models import Sample, Source
from uis import StatPanel
from utils import switch
from view_models import project_state


class StatisticsView(StatPanel):
    stat_root: wx.dataview.TreeListItem
    project_root: wx.dataview.TreeListItem
    sample_root: wx.dataview.TreeListItem
    category_root: wx.dataview.TreeListItem

    def __init__(self, parent=None):
        super().__init__(parent)

        self.stat_list.SetColumnWidth(0, 200)
        self.stat_list.SetColumnWidth(1, 200)
        self.stat_list.SetColumnWidth(2, 300)

        EventBus().register_topic(topic="switched_to_statistics", handler=self.refresh_statistics)

    def construct_stat(self):
        self.stat_list.DeleteAllItems()
        self.stat_root = self.stat_list.GetRootItem()
        self.project_root = self.stat_list.AppendItem(self.stat_root, "项目概要")
        self.sample_root = self.stat_list.AppendItem(self.stat_root, "样本")
        self.category_root = self.stat_list.AppendItem(self.stat_root, "特征标记")

    def stat_project(self):
        name = self.stat_list.AppendItem(self.project_root, "项目名称")
        self.stat_list.SetItemText(name, 1, project_state.current.name)
        output = self.stat_list.AppendItem(self.project_root, "特征输出文件")
        self.stat_list.SetItemText(output, 1, f"{project_state.current.export_file}.csv")
        output = self.stat_list.AppendItem(self.project_root, "训练样本记录文件")
        self.stat_list.SetItemText(output, 1, f"{project_state.current.export_sample}.csv")
        output = self.stat_list.AppendItem(self.project_root, "验证样本记录文件")
        self.stat_list.SetItemText(output, 1, f"{project_state.current.export_validate}.csv")
        self.stat_list.Expand(self.project_root)

    def stat_samples(self):
        groups = self.stat_list.AppendItem(self.sample_root, "样本分组")
        self.stat_list.SetItemText(groups, 1, f"{project_state.current.training_groups}")
        training = self.stat_list.AppendItem(self.sample_root, "训练样本比例")
        self.stat_list.SetItemText(training, 1, f"{project_state.current.training_rate * 100:.02f} %")
        validation = self.stat_list.AppendItem(self.sample_root, "验证样本比例")
        self.stat_list.SetItemText(validation, 1, f"{project_state.current.validation_rate * 100:.02f} %")
        size = self.stat_list.AppendItem(self.sample_root, "样本长边尺寸")
        self.stat_list.SetItemText(size, 1,
                                   f"{project_state.current.sample_long_side}")
        sample_type = self.stat_list.AppendItem(self.sample_root, "样本输出格式")
        self.stat_list.SetItemText(sample_type, 1,
                                   "PNG" if project_state.current.sample_type.lower() == "png" else "JPEG")
        total_samples = Sample.select().count()
        total_sources = Source.select().count()
        unused_sources = Source.select() \
            .where(Source.fingerprint.not_in(Sample.select(Sample.source_fingerprint))) \
            .count()
        source = self.stat_list.AppendItem(self.sample_root, "样本源数量")
        self.stat_list.SetItemText(source, 1, f"{total_sources}")
        unused_source = self.stat_list.AppendItem(self.sample_root, "未使用源数量")
        self.stat_list.SetItemText(unused_source, 1, f"{unused_sources}")
        unused_percent = self.stat_list.AppendItem(self.sample_root, "未使用源占比")
        total = self.stat_list.AppendItem(self.sample_root, "样本总数")
        self.stat_list.SetItemText(total, 1, f"{total_samples}")
        ss_rate = self.stat_list.AppendItem(self.sample_root, "源利用率")
        ss_effi = self.stat_list.AppendItem(self.sample_root, "源效率")
        if total_sources == 0:
            ss_description = ("0", "0 : 0")
            unused_description = ("0", "0 : 0")
            ss_effi_description = ("0", "0 : 0")
        else:
            from fractions import Fraction
            ss_frac = Fraction(total_samples, total_sources)
            ss_description = (f"{round(float(ss_frac), 2):.02f}", f"{ss_frac.numerator} : {ss_frac.denominator}")
            unused_frac = Fraction(unused_sources, total_sources)
            unused_description = (f"{round(float(unused_frac), 2):.02f}",
                                  f"{unused_frac.numerator} : {unused_frac.denominator}")
            ss_effi_frac = Fraction(total_samples, total_sources - unused_sources)
            ss_effi_description = (f"{round(float(ss_effi_frac), 2)}",
                                   f"{ss_effi_frac.numerator} : {ss_effi_frac.denominator}")
        self.stat_list.SetItemText(ss_rate, 1, ss_description[0])
        self.stat_list.SetItemText(ss_rate, 2, ss_description[1])
        self.stat_list.SetItemText(unused_percent, 1, unused_description[0])
        self.stat_list.SetItemText(unused_percent, 2, unused_description[1])
        self.stat_list.SetItemText(ss_effi, 1, ss_effi_description[0])
        self.stat_list.SetItemText(ss_effi, 2, ss_effi_description[1])
        evaluate = self.stat_list.AppendItem(self.sample_root, "推荐评估方法")
        group_size = total_samples // project_state.current.training_groups
        enhance_prompt, evaluate_prompt = self.evaluate_sample_amount(group_size)
        self.stat_list.SetItemText(total, 2, enhance_prompt)
        self.stat_list.SetItemText(evaluate, 1, evaluate_prompt)
        sim_group = self.stat_list.AppendItem(self.sample_root, "模拟分组概要")
        group_amount = self.stat_list.AppendItem(sim_group, "样本数量")
        self.stat_list.SetItemText(group_amount, 1, f"{group_size}")
        training_amount = self.stat_list.AppendItem(sim_group, "训练样本数量")
        self.stat_list.SetItemText(training_amount, 1,
                                   f"{round(group_size * project_state.current.training_rate):d}")
        validate_amount = self.stat_list.AppendItem(sim_group, "验证样本数量")
        self.stat_list.SetItemText(validate_amount, 1,
                                   f"{round(group_size * project_state.current.validation_rate):d}")
        self.stat_list.Expand(self.sample_root)
        self.stat_list.Expand(sim_group)

    def stat_categories(self):
        amount = self.stat_list.AppendItem(self.category_root, "特征数量")
        self.stat_list.SetItemText(amount, 1, f"{len(project_state.categories)}")
        detail_amount = self.stat_list.AppendItem(self.category_root, "样本特征统计")
        cates = Sample.categories.children().alias("cates")
        for cate in sorted({c for c in project_state.categories}):
            cate_leaf = self.stat_list.AppendItem(detail_amount, cate)
            cate_amount = Sample.select() \
                .from_(Sample, cates) \
                .where(cates.c.value == cate) \
                .count()
            prompt, _ = self.evaluate_sample_amount(cate_amount)
            self.stat_list.SetItemText(cate_leaf, 1, f"{cate_amount}")
            self.stat_list.SetItemText(cate_leaf, 2, prompt)
        self.stat_list.Expand(self.category_root)
        self.stat_list.Expand(detail_amount)

    def refresh_statistics(self, _):
        self.construct_stat()
        self.stat_project()
        self.stat_samples()
        self.stat_categories()

    def evaluate_sample_amount(self, amount: int) -> Tuple[str, str]:
        for case in switch(amount):
            if case(lambda v: v < 400):
                enhance_prompt = "不满足训练需要"
                evaluate_prompt = "重复K折验证"
            if case(lambda v: 400 <= v < 4000):
                enhance_prompt = "基本满足训练需要，但建议使用增强"
                evaluate_prompt = "重复K折验证"
            if case(lambda v: 4000 <= v < 8000):
                enhance_prompt = "基本满足训练需要，但建议使用增强"
                evaluate_prompt = "K折交叉验证"
            if case(lambda v: 8000 <= v < 14000):
                enhance_prompt = "基本满足训练需要"
                evaluate_prompt = "K折交叉验证"
            if case(lambda v: 14000 <= v < 20000):
                enhance_prompt = "基本满足训练需要"
                evaluate_prompt = "使用验证集"
            if case(lambda v: 20000 <= v):
                enhance_prompt = "满足训练需要"
                evaluate_prompt = "使用验证集"
        return enhance_prompt, evaluate_prompt
